// Tencent is pleased to support the open source community by making RapidJSON available.
// 
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed 
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
// specific language governing permissions and limitations under the License.

#ifndef RAPIDJSON_INTERNAL_META_H_
#define RAPIDJSON_INTERNAL_META_H_

#include "../rapidjson.h"

#ifdef __GNUC__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
#if defined(_MSC_VER)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(6334)
#endif

#if RAPIDJSON_HAS_CXX11_TYPETRAITS
#include <type_traits>
#endif

//@cond RAPIDJSON_INTERNAL
RAPIDJSON_NAMESPACE_BEGIN
    namespace internal {

// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching
        template<typename T>
        struct Void {
            typedef void Type;
        };

///////////////////////////////////////////////////////////////////////////////
// BoolType, TrueType, FalseType
//
        template<bool Cond>
        struct BoolType {
            static const bool Value = Cond;
            typedef BoolType Type;
        };
        typedef BoolType<true> TrueType;
        typedef BoolType<false> FalseType;


///////////////////////////////////////////////////////////////////////////////
// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr
//

        template<bool C>
        struct SelectIfImpl {
            template<typename T1, typename T2>
            struct Apply {
                typedef T1 Type;
            };
        };
        template<>
        struct SelectIfImpl<false> {
            template<typename T1, typename T2>
            struct Apply {
                typedef T2 Type;
            };
        };
        template<bool C, typename T1, typename T2>
        struct SelectIfCond : SelectIfImpl<C>::template Apply<T1, T2> {
        };
        template<typename C, typename T1, typename T2>
        struct SelectIf : SelectIfCond<C::Value, T1, T2> {
        };

        template<bool Cond1, bool Cond2>
        struct AndExprCond : FalseType {
        };
        template<>
        struct AndExprCond<true, true> : TrueType {
        };
        template<bool Cond1, bool Cond2>
        struct OrExprCond : TrueType {
        };
        template<>
        struct OrExprCond<false, false> : FalseType {
        };

        template<typename C>
        struct BoolExpr : SelectIf<C, TrueType, FalseType>::Type {
        };
        template<typename C>
        struct NotExpr : SelectIf<C, FalseType, TrueType>::Type {
        };
        template<typename C1, typename C2>
        struct AndExpr : AndExprCond<C1::Value, C2::Value>::Type {
        };
        template<typename C1, typename C2>
        struct OrExpr : OrExprCond<C1::Value, C2::Value>::Type {
        };


///////////////////////////////////////////////////////////////////////////////
// AddConst, MaybeAddConst, RemoveConst
        template<typename T>
        struct AddConst {
            typedef const T Type;
        };
        template<bool Constify, typename T>
        struct MaybeAddConst : SelectIfCond<Constify, const T, T> {
        };
        template<typename T>
        struct RemoveConst {
            typedef T Type;
        };
        template<typename T>
        struct RemoveConst<const T> {
            typedef T Type;
        };


///////////////////////////////////////////////////////////////////////////////
// IsSame, IsConst, IsMoreConst, IsPointer
//
        template<typename T, typename U>
        struct IsSame : FalseType {
        };
        template<typename T>
        struct IsSame<T, T> : TrueType {
        };

        template<typename T>
        struct IsConst : FalseType {
        };
        template<typename T>
        struct IsConst<const T> : TrueType {
        };

        template<typename CT, typename T>
        struct IsMoreConst
                : AndExpr<IsSame<typename RemoveConst<CT>::Type, typename RemoveConst<T>::Type>,
                        BoolType<IsConst<CT>::Value >= IsConst<T>::Value> >::Type {
        };

        template<typename T>
        struct IsPointer : FalseType {
        };
        template<typename T>
        struct IsPointer<T *> : TrueType {
        };

///////////////////////////////////////////////////////////////////////////////
// IsBaseOf
//
#if RAPIDJSON_HAS_CXX11_TYPETRAITS

        template <typename B, typename D> struct IsBaseOf
            : BoolType< ::std::is_base_of<B,D>::value> {};

#else // simplified version adopted from Boost

        template<typename B, typename D>
        struct IsBaseOfImpl {
            RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0);
            RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0);

            typedef char (&Yes)[1];
            typedef char (&No)[2];

            template<typename T>
            static Yes Check(const D *, T);

            static No Check(const B *, int);

            struct Host {
                operator const B *() const;

                operator const D *();
            };

            enum {
                Value = (sizeof(Check(Host(), 0)) == sizeof(Yes))
            };
        };

        template<typename B, typename D>
        struct IsBaseOf
                : OrExpr<IsSame<B, D>, BoolExpr<IsBaseOfImpl<B, D> > >::Type {
        };

#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS


//////////////////////////////////////////////////////////////////////////
// EnableIf / DisableIf
//
        template<bool Condition, typename T = void>
        struct EnableIfCond {
            typedef T Type;
        };
        template<typename T>
        struct EnableIfCond<false, T> { /* empty */ };

        template<bool Condition, typename T = void>
        struct DisableIfCond {
            typedef T Type;
        };
        template<typename T>
        struct DisableIfCond<true, T> { /* empty */ };

        template<typename Condition, typename T = void>
        struct EnableIf : EnableIfCond<Condition::Value, T> {
        };

        template<typename Condition, typename T = void>
        struct DisableIf : DisableIfCond<Condition::Value, T> {
        };

// SFINAE helpers
        struct SfinaeTag {
        };
        template<typename T>
        struct RemoveSfinaeTag;
        template<typename T>
        struct RemoveSfinaeTag<SfinaeTag &(*)(T)> {
            typedef T Type;
        };

#define RAPIDJSON_REMOVEFPTR_(type) \
    typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \
        < ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type

#define RAPIDJSON_ENABLEIF(cond) \
    typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \
        <RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL

#define RAPIDJSON_DISABLEIF(cond) \
    typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \
        <RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL

#define RAPIDJSON_ENABLEIF_RETURN(cond, returntype) \
    typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \
        <RAPIDJSON_REMOVEFPTR_(cond), \
         RAPIDJSON_REMOVEFPTR_(returntype)>::Type

#define RAPIDJSON_DISABLEIF_RETURN(cond, returntype) \
    typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \
        <RAPIDJSON_REMOVEFPTR_(cond), \
         RAPIDJSON_REMOVEFPTR_(returntype)>::Type

    } // namespace internal
RAPIDJSON_NAMESPACE_END
//@endcond

#if defined(__GNUC__) || defined(_MSC_VER)
RAPIDJSON_DIAG_POP
#endif

#endif // RAPIDJSON_INTERNAL_META_H_
